package me.yuzho.study.concurrency;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import static net.mindview.util.Print.*;



class Toast {
    public enum Status {DRY, BUTTERED, JAMMED}
    
    private Status status = Status.DRY;
    private final int id;
    
    public Toast (int idn) {
        this.id = idn;
    }
    
    public void butter() { status = Status.BUTTERED; }
    public void jam() { status = Status.JAMMED; }
    public Status getStatus() { return status; }
    public int getId () {return id;}

    @Override
    public String toString() {
        return String.format("Toast %d : %s", id, status);
    }
    
    
}


class ToastQueue extends LinkedBlockingQueue<Toast> {
    
}

class Toaster implements Runnable {
    private ToastQueue toastQueue;
    private int count =0;
    private Random rand = new Random(47);
    
    public Toaster(ToastQueue tq) {
        toastQueue = tq;
    }

    @Override
    public void run() {
       
        try {
            while(!Thread.interrupted()) {
                TimeUnit.MICROSECONDS.sleep(100 + rand.nextInt(500));
                
                // make a toast
                Toast t = new Toast(count++);
                
                print(t);
                
                //insert into queue
                toastQueue.put(t);
            }
        } catch (InterruptedException e) {
            print("Toaster interrunpted");
        }
        
        print("Toaster off");
        
    }  
    
}

//apply butter to toast
class Butterer implements Runnable {
    
    private ToastQueue dryQueue, butteredQueue;
    
    public Butterer(ToastQueue dry, ToastQueue buttered) {
        dryQueue = dry;
        butteredQueue = buttered;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                
                // Blocks until next piece of toast is available
                
                Toast t = dryQueue.take();
                t.butter();
                print(t);
                butteredQueue.put(t);
                
            }
        } catch (InterruptedException e) {

            print("Butter interrupted");
        }
        
        print("Butterer off");
    }
}

//apply jam to toast
class Jammer implements Runnable {
  
  private ToastQueue butteredQueue, finishedQueue;
  
  public Jammer(ToastQueue buttered, ToastQueue finished) {
      butteredQueue = buttered;
      finishedQueue = finished;
  }

  @Override
  public void run() {
      try {
          while(!Thread.interrupted()) {
              
              // Blocks until next piece of toast is available
              
              Toast t = butteredQueue.take();
              t.jam();
              print(t);
              finishedQueue.put(t);
              
          }
      } catch (InterruptedException e) {

          print("Jammer interrupted");
      }
      
      print("Jammer off");
  }
}

//Consume the toast;
class Eater implements Runnable {

    private ToastQueue finishedQueue;
    private int counter = 0;
    
    public Eater(ToastQueue finished) {
        finishedQueue = finished;
    }
    
    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                
                // Blocks until next piece of toast is available
                
                Toast t = finishedQueue.take();
                // Verify that the toast is coming in order
                // and that all pieces are getting jammed:
                if(t.getId() != counter++ || t.getStatus() != Toast.Status.JAMMED) {
                    print(">>>> Error: " + t);
                    System.exit(1);
                } else {
                    print("Chomp! " + t );
                }
                
                
            }
        } catch (InterruptedException e) {
    
            print("Eater interrupted");
        }
        
        print("Eater off");
    }
}

public class ToastOMatic {

    public static void main(String[] args) throws InterruptedException {
        
        ToastQueue dryQueue = new ToastQueue(),
                butteredQueue = new ToastQueue(),
                finishedQueue = new ToastQueue();
        
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new Toaster(dryQueue));
        exec.execute(new Butterer(dryQueue, butteredQueue));
        exec.execute(new Jammer(butteredQueue, finishedQueue));
        exec.execute(new Eater(finishedQueue));
        
        TimeUnit.SECONDS.sleep(5);
        
        exec.shutdownNow();
        
    }
    
}
